
#ifndef COMMON_H
#define COMMON_H

#include <vector>
#include <string>

#include <cstring>
#include <cmath>

#include <typeinfo>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>

#include <sstream>

#include <QtWiAux>

extern "C" {
#include <errno.h>
}

////////////
// Global //
////////////

extern enum StationType {

	RECEIVER, SENDER
	
} station_type;

// Value logfiles for evaluations purposes being written to /tmp/ubench/...
extern bool write_logfiles;

extern bool connected;

////////////
// Macros //
////////////


// Stringify
#define strfy(S) xstrfy(S)
#define xstrfy(S) #S

// Round double to precison
#define rndn(N, PRECISION) ({\
			register int n = (int)(N * pow(10, PRECISION));\
			(((double)n) / pow(10, PRECISION));\
		})

// Any type to const char
#define const_char(TYPE, VAR) \
	const_cast<char*>(to_string<TYPE>(VAR).c_str())

// Strings for unrepresented numeric values
#define nana(STR) \
	((!strncmp("-1", STR, 2) ||\
	  !strncmp("inf", STR, 3) || !strncmp("nan", STR, 2)) ?\
	 ("n/a") : (STR))

// Macro used in selection cases for default textbox contents
#define sndr_recv(SNDR, RECV) \
	if(station_type == SENDER)\
		dtxt = SNDR;\
	else\
		dtxt = RECV

// For use in strncmp
#define lenof(STR) STR, strlen(STR)

// Rtti type compare
#define rtti_comp(TYPE, OBJ) (typeid(TYPE) == typeid(OBJ))

// Ignored values
#define IGNORED_VAL 0


//////////////////////
// Extern constants //
//////////////////////

extern const int messy_concurrency_fixup;

extern const int main_window_width;
extern const int main_window_height;
extern const int main_widgets_width;
extern const int main_widgets_xoffset;

extern const int general_widget_height;
extern const int general_widget_buffer;

extern const int general_textbox_height;
extern const int general_button_height;

extern const int general_checkbutton_edge;

///////////////
// Templates //
///////////////

template<class T>
T string_convert(const std::string& s)
{
     std::istringstream stream (s);
     T t;
     stream >> t;
     return t;
}

template<class T>
std::string to_string(const T& t)
{
     std::ostringstream stream;
     stream << t;
     return stream.str();
}

////////////////////
// Error handling //
////////////////////

// Error state class for data transfering involed classes

class ErrorReporter
{
public:
	enum Status { OK = 0,
		      ERROR,
		      RECOVERABLE };
	
private:
	volatile std::string last_status_msg;
	IMutexLock<std::string> last_status_msg_lock;
	
	volatile std::string last_error_msg;
	IMutexLock<std::string> last_error_msg_lock;
	
	static const std::string prfx[];
	
	Status last_status;
	IMutexLock<Status> last_status_lock;
	
	volatile bool errorRaised;
	IMutexLock<bool> errorRaised_lock;

public:
	class IrrecoverableError { };

	ErrorReporter() : last_status_msg_lock(last_status_msg), last_error_msg_lock(last_error_msg),
			  last_status(OK), last_status_lock(last_status),
			  errorRaised(false), errorRaised_lock(errorRaised)
	{
		errno = 0;
	}


	bool hasErrors() const
	{
		return errorRaised_lock.read();
	}
	

	// After a status and error message has been fetched view them as being `picked up'
	// meaning that they will be reset to "".
	
	const std::string statusMsg() // todo: should be const using mutables
	{
		std::string smsg = last_status_msg_lock.read();

		//last_status_msg_lock.set("");
		
		return smsg;
	}
	
	const std::string errorMsg()
	{
		std::string emsg = last_error_msg_lock.read();

		//last_error_msg_lock.set("");

		return emsg;
	}

	const ErrorReporter::Status getStatus() const
	{
		return last_status_lock.read();
	}

protected:

	// Mirrors another ErrorReporters state by copying
	void mirror(ErrorReporter &e)
	{
		last_status_msg_lock.set(e.statusMsg());
		last_error_msg_lock.set(e.errorMsg());
		last_status_lock.set(e.getStatus());
		errorRaised_lock.set(e.hasErrors());
	}

	void status(std::string msg)
	{
		last_status_msg_lock.set(msg);
	}

	int error(ErrorReporter::Status st, std::string msg)
	{
		last_status = st;
		errorRaised_lock.set(true);

		// todo segfault: prfx[st] + " :"
		
		if(errno) msg += "\n(" + std::string(strerror(errno)) + ")";
		
		last_error_msg_lock.set(msg);
		
		return -1;
	}

	void recover() throw(IrrecoverableError)
	{
		
		if(last_status == OK)
			return;

		if(last_status == ERROR)
			throw IrrecoverableError();

		errno = 0;
		last_status = OK;
		//last_error_msg_lock.set("");
		errorRaised_lock.set(false);
	}
	
};


///////////
// Other //
///////////

class Console;
extern std::vector<std::string> tokenize(std::string, const char);
extern Console *app_console;
extern IMutexLock<Console*> app_console_lock;
extern const char *tmp_dir;
extern const char *log_dir;

typedef std::vector<std::string> vecstr;


////////////////////////
// Value logging for  //
// result evaluation  //
////////////////////////

extern "C" {
#include <sys/stat.h>
}

class ValueLogger
{
public:
	
	struct nil_stream : std::ostream
	{ 
		struct nil_buf : std::streambuf
		{ 
			int overflow(int c)
			{
				return traits_type::not_eof(c);
			} 

		} m_sbuf; 

		nil_stream() : std::ios(&m_sbuf), std::ostream(&m_sbuf)
		{ } 
	};

	static int make_dir(std::string path, bool file)
	{
		std::vector<std::string> dirs = tokenize(path, '/');

		if(file) dirs.pop_back();

		std::string now;

		std::vector<std::string>::iterator i = dirs.begin();
		for(++i; i != dirs.end(); i++) {

			now += "/" + *i;
			
			if(mkdir(now.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0 && errno != EEXIST)
				return -1;
		}

		return 0;
	}

	
private:
	std::ofstream *log;
	nil_stream *nil;
	
public:



	ValueLogger(std::string path)
	{
		std::string file_name = std::string(log_dir) + "/" + path; 
		make_dir(file_name, true);
		log = new std::ofstream(file_name.c_str(), std::ios::out | std::ios::trunc);
		if(!*log) std::cerr << "Could not open logfile\n";
		nil = new nil_stream;
	}

	std::ostream& out()
	{
		if(write_logfiles)
			return *log;

		return *nil;
	}

	~ValueLogger()
	{
		// todo
		//delete log;
	}
};



#endif
